
`include "common_header.verilog"

//  *************************************************************************
//   File : rx_deskew_buf.v
//  *************************************************************************
//   This program is controlled by a written license agreement.
//   Unauthorized Reproduction or Use is Expressly Prohibited. 
// 
//  Copyright (c) 2005 Morethanip GmbH
//  info@morethanip.com
//  *************************************************************************
//  Version: $Id: rx_dskew_buf.v,v 1.14 2017/06/07 14:37:54 dk Exp $ 
//  Author : Muhammad Anisur Rahman
//  *************************************************************************
//  Description:
// 
//  PCS deskew 
// 
//  *************************************************************************

module rx_deskew_buf (

   reset_sd0_rx_clk,
   reset_sd1_rx_clk,
   reset_sd2_rx_clk,
   reset_sd3_rx_clk,
   enable_deskew,
   rx_sync,
   deskew_error,
   buffer_rst,
   sudi_col_a,
   align_done,
   sd0_rx_clk,
  `ifdef USE_CLK_ENA
   sd0_rx_clk_ena,
  `endif    
   dec_kchar0,
   dec_data0,
   sd1_rx_clk,
  `ifdef USE_CLK_ENA
   sd1_rx_clk_ena,
  `endif    
   dec_kchar1,
   dec_data1,
   sd2_rx_clk,
  `ifdef USE_CLK_ENA
   sd2_rx_clk_ena,
  `endif    
   dec_kchar2,
   dec_data2,
   sd3_rx_clk,
  `ifdef USE_CLK_ENA
   sd3_rx_clk_ena,
  `endif    
   dec_kchar3,
   dec_data3,
   kchar0,
   data0,
   kchar1,
   data1,
   kchar2,
   data2,
   kchar3,
   data3);

input   reset_sd0_rx_clk;       //  Asynchronous Reset - sd0_rx_clk Domain
input   reset_sd1_rx_clk;       //  Asynchronous Reset - sd1_rx_clk Domain
input   reset_sd2_rx_clk;       //  Asynchronous Reset - sd2_rx_clk Domain
input   reset_sd3_rx_clk;       //  Asynchronous Reset - sd3_rx_clk Domain
input   enable_deskew;          //  Enable the deskew logic from sate machine
input   [3:0] rx_sync;          //  Receive Synchronized        
output  deskew_error;           //  Lane Alignment Error  
output  [3:0] buffer_rst;       //  Alignbuffer sync reset
output  sudi_col_a;             //  Column1= ||A|| or Column2=||A||
input   align_done;             //  Alignment Done
input   sd0_rx_clk;             //  sd0_rx_clk Domain for Lan0                       
input   [1:0] dec_kchar0;       //  Special Character Indication
input   [15:0] dec_data0;       //  Decoded Data      
input   sd1_rx_clk;             //  sd1_rx_clk Domain for Lan1  
input   [1:0] dec_kchar1;       //  Special Character Indication
input   [15:0] dec_data1;       //  Decoded Data 
input   sd2_rx_clk;             //  sd2_rx_clk Domain for Lan2  
input   [1:0] dec_kchar2;       //  Special Character Indication
input   [15:0] dec_data2;       //  Decoded Data 
input   sd3_rx_clk;             //  sd3_rx_clk Domain for Lan3  
input   [1:0] dec_kchar3;       //  Special Character Indication
input   [15:0] dec_data3;       //  Decoded Data                                                
output  [1:0] kchar0;           //  Special Character Indication
output  [15:0] data0;           //  Decoded Data      
output  [1:0] kchar1;           //  Special Character Indication
output  [15:0] data1;           //  Decoded Data 
output  [1:0] kchar2;           //  Special Character Indication
output  [15:0] data2;           //  Decoded Data 
output  [1:0] kchar3;           //  Special Character Indication
output  [15:0] data3;           // Decoded Data 

`ifdef USE_CLK_ENA
   input sd0_rx_clk_ena;        // Enable sd0_rx_clk
   input sd1_rx_clk_ena;        // Enable sd1_rx_clk
   input sd2_rx_clk_ena;        // Enable sd2_rx_clk
   input sd3_rx_clk_ena;        // Enable sd3_rx_clk
`endif

reg     deskew_error; 
wire    [3:0] buffer_rst;       //  Alignbuffer sync reset
wire    sudi_col_a; 
wire    [1:0] kchar0; 
wire    [15:0] data0; 
wire    [1:0] kchar1; 
wire    [15:0] data1; 
wire    [1:0] kchar2; 
wire    [15:0] data2; 
wire    [1:0] kchar3; 
wire    [15:0] data3; 

//  Lane 0 Buffer
//  -------------

wire    buffer_rst0;            //  Buffer Synchronous Reset
wire    rd_buffer_ack0;         //  Read Clock Acknowledge
wire    ff_wren0;               //  Buffer Write Enable 
wire    [17:0] ff_din0;         //  Buffer Input
reg     [17:0] ff_dout0;        //  Buffer Output
wire    [17:0] ff_dout_int0;    //  Buffer Output
wire    dskw_aempty0;           //  Buffer Almost Empty          

//  Lane 1 Buffer
//  -------------

wire    buffer_rst1;            //  Buffer Synchronous Reset
wire    rd_buffer_ack1;         //  Read Clock Acknowledge
wire    ff_wren1;               //  Buffer Write Enable 
wire    [17:0] ff_din1;         //  Buffer Input  
reg     [17:0] ff_dout1;        //  Buffer Output
wire    [17:0] ff_dout_int1;    //  Buffer Output
wire    dskw_aempty1;           //  Buffer Almost Empty          

//  Lane 2 Buffer
//  -------------

wire    buffer_rst2;            //  Buffer Synchronous Reset
wire    rd_buffer_ack2;         //  Read Clock Acknowledge
wire    ff_wren2;               //  Buffer Write Enable 
wire    [17:0] ff_din2;         //  Buffer Input  
reg     [17:0] ff_dout2;        //  Buffer Output
wire    [17:0] ff_dout_int2;    //  Buffer Output
wire    dskw_aempty2;           //  Buffer Almost Empty          

//  Lane 3 Buffer
//  -------------

wire    buffer_rst3;            //  Buffer Synchronous Reset
wire    rd_buffer_ack3;         //  Read Clock Acknowledge
wire    ff_wren3;               //  Buffer Write Enable 
wire    [17:0] ff_din3;         //  Buffer Input  
reg     [17:0] ff_dout3;        //  Buffer Output
wire    [17:0] ff_dout_int3;    //  Buffer Output
wire    dskw_aempty3;           //  Buffer Almost Empty  

//  Common Control
//  --------------

wire    ff_rden;                //  Buffer Read Enable
`ifdef MTIPXGXS_DPREGS
reg     ff_rden_d0;
`endif
reg     ff_rden_d;              //  Buffer Read Enable delayed
reg     a_decod;                //  /A/ on Lane Indication  
reg     sudi_col_a_int;         //  ||A|| Column Decoding    
wire    [3:0] dskw_afull;       //  Buffer Almost Full          

`ifdef USE_CLK_ENA
   reg  ff_rd_1st;              // inspect valid very first word even if clock enable deasserts.
`endif


assign buffer_rst[3:0] = {buffer_rst3, buffer_rst2, buffer_rst1, buffer_rst0}; 

//  Lane 0
//  ------

dskw_buf_wr U_WR0 (

          .reset(reset_sd0_rx_clk),
          .clk(sd0_rx_clk),
          `ifdef USE_CLK_ENA
           .clk_ena(sd0_rx_clk_ena),
          `endif
          .rx_sync(rx_sync),
          .enable_deskew(enable_deskew),
          .align_done(align_done),
          .kchar(dec_kchar0[0]),
          .data(dec_data0[7:0]),
          .buffer_rst(buffer_rst0),
          .rd_buffer_ack(rd_buffer_ack0),
          .ff_afull(dskw_afull),
          .ff_wren(ff_wren0));
          
assign ff_din0 = {dec_kchar0, dec_data0};

a_fifo_24_xgxs #(18,4,16,4,1) U_BUF0 (

          .reset_wclk(reset_sd0_rx_clk),
          .reset_rclk(reset_sd0_rx_clk),
          .wclk(sd0_rx_clk),
          `ifdef USE_CLK_ENA
           .wclk_ena(sd0_rx_clk_ena),
          `endif          
          .sync_wr_rst(buffer_rst0),
          .wren(ff_wren0),
          .din(ff_din0),
          .rclk(sd0_rx_clk),
          `ifdef USE_CLK_ENA
           .rclk_ena(sd0_rx_clk_ena),
          `endif
          .sync_rd_rst(buffer_rst0),
          .sync_rd_ack(rd_buffer_ack0),
          .rden(ff_rden),
          .dout(ff_dout_int0),
          .afull(dskw_afull[0]),
          .aempty(dskw_aempty0));
          

`ifdef MTIPXGXS_DPREGS

    always@(posedge reset_sd0_rx_clk or posedge sd0_rx_clk)
    begin

        if (reset_sd0_rx_clk==1'b1)
        begin
        
                ff_dout0 <= 18'h0;
                
        end
        else
        begin
        
         `ifdef USE_CLK_ENA
            if(sd0_rx_clk_ena == 1'b 1)
            begin
         `endif                
                
                ff_dout0 <= ff_dout_int0 ;
                
         `ifdef USE_CLK_ENA
            end
         `endif        
        
        end
        
    end  

`else

    always@( ff_dout_int0 )
    begin
                ff_dout0 = ff_dout_int0 ;
    end

`endif

//  Lane 1
//  ------

dskw_buf_wr U_WR1 (

          .reset(reset_sd1_rx_clk),
          .clk(sd1_rx_clk),
          `ifdef USE_CLK_ENA
           .clk_ena(sd1_rx_clk_ena),
          `endif          
          .rx_sync(rx_sync),
          .enable_deskew(enable_deskew),
          .align_done(align_done),
          .kchar(dec_kchar1[0]),
          .data(dec_data1[7:0]),
          .buffer_rst(buffer_rst1),
          .rd_buffer_ack(rd_buffer_ack1),
          .ff_afull(dskw_afull),
          .ff_wren(ff_wren1));

assign ff_din1 = {dec_kchar1, dec_data1};

a_fifo_24_xgxs #(18,4,16,4,1) U_BUF1 (

          .reset_wclk(reset_sd1_rx_clk),
          .reset_rclk(reset_sd0_rx_clk),
          .wclk(sd1_rx_clk),
          `ifdef USE_CLK_ENA
           .wclk_ena(sd1_rx_clk_ena),
          `endif           
          .sync_wr_rst(buffer_rst1),
          .wren(ff_wren1),
          .din(ff_din1),
          .rclk(sd0_rx_clk),
          `ifdef USE_CLK_ENA
           .rclk_ena(sd0_rx_clk_ena),
          `endif          
          .sync_rd_rst(buffer_rst1),
          .sync_rd_ack(rd_buffer_ack1),
          .rden(ff_rden),
          .dout(ff_dout_int1),
          .afull(dskw_afull[1]),
          .aempty(dskw_aempty1));
          
`ifdef MTIPXGXS_DPREGS

    always@(posedge reset_sd0_rx_clk or posedge sd0_rx_clk)
    begin

        if (reset_sd0_rx_clk==1'b1)
        begin
        
                ff_dout1 <= 18'h0;
                
        end
        else
        begin
        
         `ifdef USE_CLK_ENA
            if(sd0_rx_clk_ena == 1'b 1)
            begin
         `endif                
                
                ff_dout1 <= ff_dout_int1 ;
                
         `ifdef USE_CLK_ENA
            end
         `endif        
        
        end
        
    end  

`else

    always@( ff_dout_int1 )
    begin
                ff_dout1 = ff_dout_int1 ;
    end

`endif



//  Lane 2
//  ------

dskw_buf_wr U_WR2 (	

          .reset(reset_sd2_rx_clk),
          .clk(sd2_rx_clk),
          `ifdef USE_CLK_ENA
           .clk_ena(sd2_rx_clk_ena),
          `endif           
          .rx_sync(rx_sync),
          .enable_deskew(enable_deskew),
          .align_done(align_done),
          .kchar(dec_kchar2[0]),
          .data(dec_data2[7:0]),
          .buffer_rst(buffer_rst2),
          .rd_buffer_ack(rd_buffer_ack2),
          .ff_afull(dskw_afull),
          .ff_wren(ff_wren2));

assign ff_din2 = {dec_kchar2, dec_data2};

a_fifo_24_xgxs #(18,4,16,4,1) U_BUF2 (

          .reset_wclk(reset_sd2_rx_clk),
          .reset_rclk(reset_sd0_rx_clk),
          .wclk(sd2_rx_clk),
          `ifdef USE_CLK_ENA
           .wclk_ena(sd2_rx_clk_ena),
          `endif           
          .sync_wr_rst(buffer_rst2),
          .wren(ff_wren2),
          .din(ff_din2),
          .rclk(sd0_rx_clk),
          `ifdef USE_CLK_ENA
           .rclk_ena(sd0_rx_clk_ena),
          `endif
          .sync_rd_rst(buffer_rst2),
          .sync_rd_ack(rd_buffer_ack2),
          .rden(ff_rden),
          .dout(ff_dout_int2),
          .afull(dskw_afull[2]),
          .aempty(dskw_aempty2));
          
`ifdef MTIPXGXS_DPREGS

    always@(posedge reset_sd0_rx_clk or posedge sd0_rx_clk)
    begin

        if (reset_sd0_rx_clk==1'b1)
        begin
        
                ff_dout2 <= 18'h0;
                
        end
        else
        begin
        
         `ifdef USE_CLK_ENA
            if(sd0_rx_clk_ena == 1'b 1)
            begin
         `endif                
                
                ff_dout2 <= ff_dout_int2 ;
                
         `ifdef USE_CLK_ENA
            end
         `endif        
        
        end
        
    end  

`else

    always@( ff_dout_int2 )
    begin
                ff_dout2 = ff_dout_int2 ;
    end

`endif

//  Lane 3
//  ------

dskw_buf_wr U_WR3 (

          .reset(reset_sd3_rx_clk),
          .clk(sd3_rx_clk),
          `ifdef USE_CLK_ENA
           .clk_ena(sd3_rx_clk_ena),
          `endif             
          .rx_sync(rx_sync),
          .enable_deskew(enable_deskew),
          .align_done(align_done),
          .kchar(dec_kchar3[0]),
          .data(dec_data3[7:0]),
          .buffer_rst(buffer_rst3),
          .rd_buffer_ack(rd_buffer_ack3),
          .ff_afull(dskw_afull),
          .ff_wren(ff_wren3));

assign ff_din3 = {dec_kchar3, dec_data3};

a_fifo_24_xgxs #(18,4,16,4,1) U_BUF3 (

          .reset_wclk(reset_sd3_rx_clk),
          .reset_rclk(reset_sd0_rx_clk),
          .wclk(sd3_rx_clk),
          `ifdef USE_CLK_ENA
           .wclk_ena(sd3_rx_clk_ena),
          `endif
          .sync_wr_rst(buffer_rst3),
          .wren(ff_wren3),
          .din(ff_din3),
          .rclk(sd0_rx_clk),
          `ifdef USE_CLK_ENA
           .rclk_ena(sd0_rx_clk_ena),
          `endif          
          .sync_rd_rst(buffer_rst3),
          .sync_rd_ack(rd_buffer_ack3),
          .rden(ff_rden),
          .dout(ff_dout_int3),
          .afull(dskw_afull[3]),
          .aempty(dskw_aempty3));
          
`ifdef MTIPXGXS_DPREGS

    always@(posedge reset_sd0_rx_clk or posedge sd0_rx_clk)
    begin

        if (reset_sd0_rx_clk==1'b1)
        begin
        
                ff_dout3 <= 18'h0;
                
        end
        else
        begin
        
         `ifdef USE_CLK_ENA
            if(sd0_rx_clk_ena == 1'b 1)
            begin
         `endif                
                
                ff_dout3 <= ff_dout_int3 ;
                
         `ifdef USE_CLK_ENA
            end
         `endif        
        
        end
        
    end        

`else

    always@( ff_dout_int3 )
    begin
                ff_dout3 = ff_dout_int3 ;
    end

`endif



//  Common Read Control
//  -------------------

dskw_buf_rd U_RD (

          .reset(reset_sd0_rx_clk),
          .clk(sd0_rx_clk),
          `ifdef USE_CLK_ENA
           .clk_ena(sd0_rx_clk_ena),
          `endif
          .enable_deskew(enable_deskew),
          .deskew_error(deskew_error),
          .align_done(align_done),
          .dskw_aempty0(dskw_aempty0),
          .dskw_aempty1(dskw_aempty1),
          .dskw_aempty2(dskw_aempty2),
          .dskw_aempty3(dskw_aempty3),
          .ff_rden(ff_rden));

//  Lane 0
//  ------   

assign kchar0 = ff_dout0[17:16];
assign data0  = ff_dout0[15:0]; 

//  Lane 1
//  ------

assign kchar1 = ff_dout1[17:16]; 
assign data1  = ff_dout1[15:0]; 

//  Lane 2
//  ------

assign kchar2 = ff_dout2[17:16]; 
assign data2  = ff_dout2[15:0]; 

//  Lane 3
//  ------

assign kchar3 = ff_dout3[17:16]; 
assign data3  = ff_dout3[15:0]; 

//  ||A|| Detection
//  ---------------

always @(posedge reset_sd0_rx_clk or posedge sd0_rx_clk)
   begin : process_1
   if (reset_sd0_rx_clk == 1'b 1)
      begin
        sudi_col_a_int <= 1'b 0;   
        ff_rden_d <= 1'b 0;

        `ifdef MTIPXGXS_DPREGS
        ff_rden_d0 <= 1'b 0;
        `endif

        `ifdef USE_CLK_ENA
        ff_rd_1st <= 1'b 0;     // special treatment for very first read in combination with clock enable deassertion
        `endif

      end
   else
      begin
      
         `ifdef USE_CLK_ENA
            if(sd0_rx_clk_ena == 1'b 1 | ff_rd_1st == 1'b 1)
            begin
                ff_rd_1st <= ff_rden & ~ff_rden_d;  // keep alive very first after read started as memory output will advance even when clk_ena=0.
         `endif      
      
                `ifdef MTIPXGXS_DPREGS
                ff_rden_d0 <= ff_rden;
                ff_rden_d  <= ff_rden_d0 & ff_rden;     // latency of 2 if output registers
                `else
                ff_rden_d  <= ff_rden;                  // latency of 1
                `endif
      
      
              if (ff_rden_d==1'b 1 &
              ff_dout0[16] == 1'b 1 & ff_dout1[16] == 1'b 1 & 
              ff_dout2[16] == 1'b 1 & ff_dout3[16] == 1'b 1 & 
              ff_dout0[7:0] == 8'h 7C & ff_dout1[7:0] == 8'h 7C & 
              ff_dout2[7:0] == 8'h 7C & ff_dout3[7:0] == 8'h 7C)
                 begin
                 sudi_col_a_int <= 1'b 1;   
                 end
              else if (ff_rden_d==1'b 1 &
              ff_dout0[17] == 1'b 1 & ff_dout1[17] == 1'b 1 & 
              ff_dout2[17] == 1'b 1 & ff_dout3[17] == 1'b 1 & 
              ff_dout0[15:8] == 8'h 7C & ff_dout1[15:8] == 8'h 7C & 
              ff_dout2[15:8] == 8'h 7C & ff_dout3[15:8] == 8'h 7C)
                 begin
                 sudi_col_a_int <= 1'b 1;   
                 end
              else
                 begin
                 sudi_col_a_int <= 1'b 0;   
                 end
      
         `ifdef USE_CLK_ENA
            end
         `endif       
      
      end
   end

assign sudi_col_a = sudi_col_a_int & (!rd_buffer_ack0);

//  Error Decoding
//  --------------

always @(posedge reset_sd0_rx_clk or posedge sd0_rx_clk)
   begin : process_2
   if (reset_sd0_rx_clk == 1'b 1)
      begin
      a_decod <= 1'b 0;   
      end
   else
      begin
      
         `ifdef USE_CLK_ENA
            if(sd0_rx_clk_ena == 1'b 1)
            begin
         `endif       
      
              if (ff_rden_d==1'b 1 & (
                        ff_dout0[16] == 1'b 1 & ff_dout0[7:0] == 8'h 7C | 
                        ff_dout1[16] == 1'b 1 & ff_dout1[7:0] == 8'h 7C | 
                        ff_dout2[16] == 1'b 1 & ff_dout2[7:0] == 8'h 7C | 
                        ff_dout3[16] == 1'b 1 & ff_dout3[7:0] == 8'h 7C |
                        
                        ff_dout0[17] == 1'b 1 & ff_dout0[15:8] == 8'h 7C | 
                        ff_dout1[17] == 1'b 1 & ff_dout1[15:8] == 8'h 7C | 
                        ff_dout2[17] == 1'b 1 & ff_dout2[15:8] == 8'h 7C | 
                        ff_dout3[17] == 1'b 1 & ff_dout3[15:8] == 8'h 7C))
                 begin
                 a_decod <= 1'b 1;   
                 end
              else
                 begin
                 a_decod <= 1'b 0;   
                 end
      
         `ifdef USE_CLK_ENA
            end
         `endif      
      
      end
   end

always @(posedge reset_sd0_rx_clk or posedge sd0_rx_clk)
   begin : process_3
   if (reset_sd0_rx_clk == 1'b 1)
      begin
      deskew_error      <= 1'b 0;
      end
   else
      begin

         `ifdef USE_CLK_ENA
            if(sd0_rx_clk_ena == 1'b 1)
            begin
         `endif
      
              if ((a_decod == 1'b 1 & sudi_col_a_int == 1'b 0) | rd_buffer_ack0==1'b 1)
                 begin
                 deskew_error <= 1'b 1;
                 end
              else
                 begin
                 deskew_error <= 1'b 0;
                 end
      
         `ifdef USE_CLK_ENA
            end
         `endif
      
      end
   end

endmodule // module rx_deskew_buf